package quarto.view.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Scanner;

import quarto.Message;
import quarto.controller.client.Client;
import quarto.controller.client.ComputerPlayer;
import quarto.controller.client.HumanPlayer;
import quarto.controller.client.Player;
import quarto.controller.client.strategy.*;
import quarto.model.InvalidPieceValueException;
import quarto.model.Piece;

/**
 * Textuele User Interface voor de client
 * @knows
 * - De Client
 * @does
 * - Client aanmaken die een verbinding maakt met de Server
 * - Textuele representatie van het spel weergeven
 * - Geeft aan Client door welke handelingen de Player verricht
 * @author Frank Borgonjen & Tim Smit
 */
public class ClientTUI implements ClientUI
{
    /** de client */
    private Client client;
    
    /** de speler */
    private Player player;
    
    /** de naam van de speler */
    private String playerName;
    
    /** soort van speler, computer of mens */
    private String kindOfPlayer;
    
    /** soort van strategy */
    private String kindOfStrategy;
    
    /** de beschikbare strategies voor de computer player */
    private HashMap<String,Strategy> strategies;
    
    /** het host adres van de server */
    private InetAddress hostAddress;
    
    /** het poortnummer van de host */
    private int portNumber;
    
    /**
     * is er al een connectie geweest, 
     * om voor een tweede keer makkelijker een nieuw spelletje
     * te starten
     */
    private boolean hasSettings;
    
    /** string representatie van true */
    public static final String BOOL_JA  = "ja";
    
    /** string representatie van false */
    public static final String BOOL_NEE = "nee";
    
    /**
     * Construeert een ClientTUI object
     */
    public ClientTUI()
    {
        this.playerName   = null;
        this.kindOfPlayer = null;
        this.hostAddress  = null;
        this.portNumber   = -1;
        this.hasSettings  = false;
        
        // strategieen
        this.strategies = new HashMap<String,Strategy>();
        this.strategies.put("dumb", new DumbStrategy());
        this.strategies.put("smart", new SmartStrategy());
        this.strategies.put("perfect", new PerfectStrategy());
        
        // connect
        this.connect();
    }

    /**
     * Maakt een Client Object aan. Deze maakt een verbinding met de server
     */
    public void connect()
    {
        // als er nog geen instelling bekend zijn
        if (!this.hasSettings)
        {
            // Scanner om input van commandline te lezen
            Scanner input = new Scanner(System.in);
            
            // de naam opvragen
            do
            {
                System.out.print(Message.CLIENTTUI_QUESTION_NAME + "? \n> ");
                this.playerName = input.hasNextLine() ? input.nextLine() : null;
            }
            while (this.playerName == null);
            
            // soort speler opvragen
            do
            {
                System.out.print(Message.CLIENTTUI_QUESTION_KIND_OF_PLAYER + "? [" + Client.COMPUTER + ", " + Client.HUMAN + "] \n> ");
                this.kindOfPlayer = input.hasNextLine() ? input.nextLine() : null;
            }
            while (this.kindOfPlayer == null || !this.kindOfPlayer.equals(Client.COMPUTER) && !this.kindOfPlayer.equals(Client.HUMAN));
            
            // nieuwe speler aan maken
            if (this.kindOfPlayer.equals(Client.HUMAN))
            {
                this.player = new HumanPlayer();
            }
            else
            {
                player = new ComputerPlayer();
                
                do
                {
                    System.out.print(Message.CLIENTTUI_QUESTION_STRATEGY + "? " + this.strategies.keySet() + " \n> ");
                    this.kindOfStrategy = input.hasNextLine() ? input.nextLine() : null;
                }
                while (this.kindOfStrategy == null || !this.strategies.containsKey(this.kindOfStrategy));
                
                // strategie toevoegen aan player
                Strategy s = this.strategies.get(this.kindOfStrategy);
                ((ComputerPlayer)player).setStrategy(s);
                
            }

            // vragen naar het hostadres
            String tmpHostName;
            do
            {
                System.out.println(Message.CLIENTTUI_QUESTION_HOST + "? \n>");
                tmpHostName = input.hasNextLine() ? input.nextLine() : null;

                try
                {
                     this.hostAddress = InetAddress.getByName(tmpHostName);
                }
                catch (UnknownHostException e)
                {
                    this.errorMessage(String.format(Message.ERROR_INVALID_HOST, tmpHostName));
                }
            }
            while (hostAddress == null);
            
            // vragen naar het poortnummer
            String tmpPortNumber;
            do 
            {
                System.out.println(Message.CLIENTTUI_QUESTION_PORT + "? \n> ");
                tmpPortNumber = input.hasNextLine() ? input.nextLine() : null;

                try
                {
                    this.portNumber = Integer.parseInt(tmpPortNumber);
                }
                catch (NumberFormatException e)
                {
                    this.errorMessage(String.format(Message.ERROR_INVALID_PORT, tmpPortNumber));
                }
            }
            while (this.portNumber == -1);
            
            // de instellingen zijn bekend
            this.hasSettings = true;
        }
        
        // even laten merken wat we aan het doen zijn
        this.addMessage(Message.CLIENTUI_CONNECTING);
        
        try
        {
            // we proberen een nieuwe client aan te maken
            this.client = new Client(this.playerName, this.hostAddress, this.portNumber, this, player);
        
            this.player.setClient(client);
            this.client.start();

            // even wat communiceren naar de gebruiker
            addMessage(Message.CLIENTUI_CONNECTED);
        }
        // aanmaken van nieuwe client is mislukt
        catch (IOException e)
        {
            // foutmelding printen
            this.errorMessage(e.getMessage());
        }
        catch (IllegalArgumentException e)
        {
            // foutmelding printen
            this.errorMessage(e.getMessage());
        }
    }
    
    /**
     * Vraagt de speler om opnieuw te connecten
     */
    public void askForReconnect()
    {
        Scanner tmpScanner = new Scanner(System.in);
        String antwoord; 
        do 
        {
            System.out.print("\n" + Message.CLIENTTUI_QUESTION_RECONNECT + "? (" + BOOL_JA + " of " + BOOL_NEE + ") ");
            antwoord = tmpScanner.hasNextLine() ? tmpScanner.nextLine() : null;
        } 
        while (antwoord == null ||
              (!antwoord.equals(BOOL_JA) && !antwoord.equals(BOOL_NEE)));                  
        
        // als gebruiker opnieuw wilt verbinden, dan doen we dat. Anders
        // sluiten we de clientUI af
        if (antwoord.equals(BOOL_JA))
        {
            this.connect();
        }
        else
        {
            System.exit(0);
        }
    }

    /**
     * Laat het board uit de uitvoer zien
     */
    public void showBoard()
    {
        this.addMessage(client.getBoard().toString());
    }

    /**
     * Vraagt om de locatie voor het meegegeven stuk
     * @param piece
     * @return de locatie
     */
    public int getLocationForPiece(Piece piece)
    {
        // initialisatie
        Scanner tmpScanner;
        String tmpResult;
        int result = -1;
        
        do
        {
            // bericht naar de gebruiker toe
            System.out.print(String.format(Message.CLIENTTUI_QUESTION_LOCATION, piece.getDescription()));
            tmpScanner = new Scanner(System.in);
            tmpResult = tmpScanner.hasNextLine() ? tmpScanner.nextLine() : null;
            
            try
            {
                result = Integer.parseInt(tmpResult);
            }
            catch (NumberFormatException ex) {}
        }
        // zolang er geen geldig getal is ingevoerd
        while (result == -1);

        return result;
    }
    
    /**
     * Vraagt de gebruiker om een nieuw stuk
     * @return het nieuwe stuk
     */
    public Piece getNewPiece()
    {
        // initialisatie
        Piece newPiece = null;
        String tmpResult;
        int tmpIntResult;
        Scanner tmpScanner;
        
        do
        {
            // bericht naar de gebruiker
            System.out.print(Message.CLIENTTUI_QUESTION_PIECE);
            tmpScanner = new Scanner(System.in);
            tmpResult = tmpScanner.hasNextLine() ? tmpScanner.nextLine() : null;
            
            try
            {
                tmpIntResult = Integer.parseInt(tmpResult);
                newPiece = new Piece(tmpIntResult);
            }
            catch (NumberFormatException ex) {}
            catch (InvalidPieceValueException ex) {}
        }
        // zolang er geen geldig stuk is
        while (newPiece == null);
        
        return newPiece;
    }
    
    /**
     * Voegt een statusbericht toe
     * @param msg het bericht dat toegevoegd moet worden
     */
    public void addMessage(String msg)
    {
        System.out.println(msg);
    }
    
    /**
     * Geeft een error bericht
     * @param msg de foutmelding
     */
    public void errorMessage(String msg)
    {
        this.addMessage("[ERROR] " + msg);
    }
    
    /** Levert het Internetadres van deze computer op. */
    private String getHostAddress()
    {
        try
        {
            InetAddress iaddr = InetAddress.getLocalHost();
            return iaddr.getHostAddress();
        }
        catch (UnknownHostException e)
        {
            return "?unknown?";
        }
    }
    
    /**
     * Maakt een ClientTUI aan
     */
    public static void main(String[] args)
    {
        new ClientTUI();
    }
}
